//*************************************************************************************
// EGTB Generator Lowest Level Access Functions for Raw Uncompressed database files
//*************************************************************************************

#include "Stdafx.h"	
#include "EndGameDb.h"

namespace EndGameDB
{
	// Raw Database Path
	wchar_t szRawDatabaseDir[MAX_PATH];

	// the currently computed database pointers
	uint32 *currentDb_b, *currentDb_w;  
	uint8 *dtm_W, *dtm_B;

	// memory panic helper variables
	bool bMemoryPanicInProgress = false;
	int64 countThreadSleep = 0;
	omp_lock_t lockMemoryPanic;


	// memory variables
	int64 memInUse = 0;
	int64 memoryLimit = 0; 

	// subdatabase records
	subdb SubDatabase[MAXPIECE+1][MAXPIECE+1][MAXPIECE+1][MAXPIECE+1][9][9][2];



	// settings functions
	/////////////////////////////////////////////////////////

	void SetDatabaseRawDirectory(const wchar_t* lpDir)
	{
		wcscpy_s(szRawDatabaseDir, PATH_MAX, lpDir);
	}


	void SetDbRawMemoryLimit(int64 memoryLimitInMB)
	{
		memoryLimit = memoryLimitInMB * 1024 * 1024;
	}


	// low-level access functios to the raw data of databases
	/////////////////////////////////////////////////////////
	
	uint32 GetDatabaseValue(uint32 *database, int64 index)
	{
		uint32 value=UNKNOWN;
	
		#pragma omp critical
		value = database[index/16];

		value = (value >> (2*(index%16)) ) & 3;

		return value;
	}

	uint32 GetDatabaseValueNOMP(uint32 *database, int64 index)
	{
		uint32 value=UNKNOWN;
	
		value = database[index/16];

		value = (value >> (2*(index%16)) ) & 3;

		return value;
	}


	void SetDatabaseValue(uint32 *database, int64 index, uint32 value)
	{
		uint32 tmp;
		uint32 mask;

		#pragma omp critical
		tmp = database[index/16];
	
		mask = 3 << (2*(index%16));
		mask = ~mask;
		tmp &= mask;

		tmp+= value << (2*(index%16));

		#pragma omp critical
		database[index/16] = tmp;
	}


	void SetDatabaseValueNOMP(uint32 *database, int64 index, uint32 value)
	{
		uint32 tmp;
		uint32 mask;

		tmp = database[index/16];
	
		mask = 3 << (2*(index%16));
		mask = ~mask;
		tmp &= mask;

		tmp+= value << (2*(index%16));

		database[index/16] = tmp;
	}


	uint8 GetDTMValue(uint8* dtm, int64 index)
	{
		uint8 value;

		#pragma omp critical
		value = dtm[index];

		return value;
	}


	uint8 GetDTMValueNOMP(uint8* dtm, int64 index)
	{
		uint8 value;
		value = dtm[index];
		return value;
	}

	
	void SetDTMValue(uint8* dtm, int64 index, uint8 value)
	{
		#pragma omp critical
		dtm[index] = value;
	}


	void SetDTMValueNOMP(uint8* dtm, int64 index, uint8 value)
	{
		dtm[index] = value;
	}


	bool IsSymmetric(int wm, int wk, int bm, int bk, int wmrank, int bmrank)
	{
		if(bm==wm && bk==wk && bmrank==wmrank)
			return true;
		else
			return false;
	}

	uint32* LoadRawDatabase(int wm, int wk, int bm, int bk, int wmrank, int bmrank, int color, uint8** ppDtm)
	{
		wchar_t dbname[PATH_MAX];
		uint32* database = SubDatabase[wm][wk][bm][bk][wmrank][bmrank][color].database;
		uint8* dtm = NULL;
		int64 memsize = 0;
		FILE* fp;

		if(database == NULL)
		{
			WATCH_START(T_FILE_READ);

			bool bRepeat = true;

			alloc:;
			int64 dbsize = SubDatabase[wm][wk][bm][bk][wmrank][bmrank][color].databasesize;
			memsize = Pad16(dbsize/4);
			database = (uint32*)(new uint8[(unsigned int)memsize]);
			dtm = new uint8[(unsigned long)dbsize];

			memInUse += memsize + dbsize;

			if(database == NULL || dtm == NULL)
			{
				if(database != NULL)
					delete[] database;
				if(dtm != NULL)
					delete[] dtm;

				if(bRepeat == false)
				{
					database  = NULL;
					dtm = NULL;
					goto Exit;
				}


				MemoryPanic();
				bRepeat = false;
				goto alloc;
			}

			if(memInUse > memoryLimit)
			{
				MemoryPanic();
				// goto alloc;
			}

			SubDatabase[wm][wk][bm][bk][wmrank][bmrank][color].database = database;
			SubDatabase[wm][wk][bm][bk][wmrank][bmrank][color].db_dtm = dtm;
			#pragma omp flush


			// read DB
			SetDbName(wm, wk, bm, bk, wmrank, bmrank, color, dbname);

			_wfopen_s(&fp, dbname, L"rb");
			if(fp == NULL)
			{
				//wchar_t str[PATH_MAX];
				//swprintf_s(str, PATH_MAX, L"Could not open database - %s", dbname);
				//throw gcnew ApplicationException(gcnew System::String(str));
			}
			else
			{
				fread(database, 1, (size_t)memsize, fp);
				fclose(fp);
			}

			// read DTM
			SetDbDTMName(wm, wk, bm, bk, wmrank, bmrank, color, dbname);

			_wfopen_s(&fp, dbname, L"rb");
			if(fp == NULL)
			{
				//wchar_t str[PATH_MAX];
				//swprintf_s(str, PATH_MAX, L"Could not open database - %s", dbname);
				//throw gcnew ApplicationException(gcnew System::String(str));
			}
			else
			{
				fread(dtm, 1, (size_t)dbsize, fp);
				fclose(fp);
			}

			*ppDtm = dtm;
Exit:;
			WATCH_END(T_FILE_READ);

		} // if

		return database;
	}
	
	uint32* GetRawDatabase(int wm, int wk, int bm, int bk, int wmrank, int bmrank, int color, uint8** ppDtm)
	{
		uint32* database = NULL;
		database = SubDatabase[wm][wk][bm][bk][wmrank][bmrank][color].database;
		*ppDtm = SubDatabase[wm][wk][bm][bk][wmrank][bmrank][color].db_dtm;
	
		// if database is not loaded, allocate memory for it, 
		// do synchronized double-check
		if(database == NULL)
		{
			#pragma omp critical
			{
				database = LoadRawDatabase(wm, wk, bm, bk, wmrank, bmrank, color, ppDtm);
			} // omp-critical double check
		} // if

		return database;
	}


	uint32* GetRawDatabaseNOMP(int wm, int wk, int bm, int bk, int wmrank, int bmrank, int color, uint8** ppDtm)
	{
		uint32* database = NULL;
		database = SubDatabase[wm][wk][bm][bk][wmrank][bmrank][color].database;
		*ppDtm = SubDatabase[wm][wk][bm][bk][wmrank][bmrank][color].db_dtm;
	
		// if database is not loaded, allocate memory for it, 
		if(database == NULL)
		{
			database = LoadRawDatabase(wm, wk, bm, bk, wmrank, bmrank, color, ppDtm);
		} // if

		return database;
	}


	// memory panic helper functions
	/////////////////////////////////////////////////////////


	void UnloadAll()
	{
		memInUse = 0;
		currentDb_b = NULL;
		currentDb_w = NULL;
		omp_destroy_lock(&lockMemoryPanic);

		for(int i=0;i<=MAXPIECE;i++)
		{
			for(int j=0;j<=MAXPIECE;j++)
			{
				for(int k=0;k<=MAXPIECE;k++)
				{
					for(int l=0;l<=MAXPIECE;l++)
					{
						for(int m=0;m<9;m++)
						{
							for(int n=0;n<9;n++)
							{
								// black databases
								if(SubDatabase[i][j][k][l][m][n][0].database != NULL) 
								{	
									delete[] SubDatabase[i][j][k][l][m][n][0].database;
									SubDatabase[i][j][k][l][m][n][0].database=NULL;

									if(SubDatabase[i][j][k][l][m][n][0].db_dtm != NULL)
										delete[] SubDatabase[i][j][k][l][m][n][0].db_dtm;

									SubDatabase[i][j][k][l][m][n][0].db_dtm = NULL;
								}

								// white databases
								if(SubDatabase[i][j][k][l][m][n][1].database != NULL) 
								{
									delete[] SubDatabase[i][j][k][l][m][n][1].database;
									SubDatabase[i][j][k][l][m][n][1].database=NULL;
									if(SubDatabase[i][j][k][l][m][n][1].db_dtm != NULL)
										delete[] SubDatabase[i][j][k][l][m][n][1].db_dtm;
									SubDatabase[i][j][k][l][m][n][1].db_dtm = NULL;
								}
							}
						}
					}
				}
			}
		}
	}


	void MemoryPanic()
	{
		int i,j,k,l,m,n;


		countThreadSleep = 0;
		omp_set_lock(&lockMemoryPanic);
		bMemoryPanicInProgress = true;

		#pragma omp flush(countThreadSleep)
		#pragma omp flush(bMemoryPanicInProgress)
	
		// wait for threads, bad active checking, needs to think off something better
		while(countThreadSleep < countThreadSleep - 1)
		{}



		memInUse = 0;
		for(i=0;i<=MAXPIECE;i++)
		{
			for(j=0;j<=MAXPIECE;j++)
			{
				for(k=0;k<=MAXPIECE;k++)
				{
					for(l=0;l<=MAXPIECE;l++)
					{
						for(m=0;m<9;m++)
						{
							for(n=0;n<9;n++)
							{
								// black databases
								if(SubDatabase[i][j][k][l][m][n][0].database != NULL) 
									{
									if(SubDatabase[i][j][k][l][m][n][0].database != currentDb_b) 
									{
										delete[] SubDatabase[i][j][k][l][m][n][0].database;
										SubDatabase[i][j][k][l][m][n][0].database=NULL;
										if(SubDatabase[i][j][k][l][m][n][0].db_dtm != NULL)
											delete[] SubDatabase[i][j][k][l][m][n][0].db_dtm;
										SubDatabase[i][j][k][l][m][n][0].db_dtm = NULL;
									}
									else
									{
										memInUse += Pad16(SubDatabase[i][j][k][l][m][n][0].databasesize/4);
										memInUse += SubDatabase[i][j][k][l][m][n][0].databasesize;
									}

								}

								// white databases
								if(SubDatabase[i][j][k][l][m][n][1].database != NULL) 
								{
									if(SubDatabase[i][j][k][l][m][n][1].database != currentDb_w) 
									{
										delete[] SubDatabase[i][j][k][l][m][n][1].database;
										SubDatabase[i][j][k][l][m][n][1].database=NULL;
										if(SubDatabase[i][j][k][l][m][n][1].db_dtm != NULL)
											delete[] SubDatabase[i][j][k][l][m][n][1].db_dtm;
										SubDatabase[i][j][k][l][m][n][1].db_dtm=NULL;
									}
									else
									{
										memInUse += Pad16(SubDatabase[i][j][k][l][m][n][1].databasesize/4);
										memInUse += SubDatabase[i][j][k][l][m][n][1].databasesize;
									}

								}
							}
						}
					}
				}
			}
		}

		#pragma omp flush

		bMemoryPanicInProgress = false;
		countThreadSleep = 0;
		omp_unset_lock(&lockMemoryPanic);
	}


	// file name creation functions
	/////////////////////////////////////////////////////////		

	void SetDbName(int wm, int wk, int bm, int bk, int wmrank, int bmrank, int color, wchar_t *dbname)
	{
		wcscpy_s(dbname, PATH_MAX, szRawDatabaseDir);
		int len = (int)wcslen(dbname);
		if(dbname[len - 1] != L'\\')
		{
			wcscat_s(dbname, PATH_MAX, L"\\");
		}

		wchar_t szFileName[PATH_MAX];

		if(color == BLACK)
			swprintf_s(szFileName, PATH_MAX,L"db%i%i%i%i-%i%ib.dat", wm,wk,bm,bk,wmrank,bmrank);
		else
			swprintf_s(szFileName, PATH_MAX, L"db%i%i%i%i-%i%iw.dat", wm,wk,bm,bk,wmrank,bmrank);

		wcscat_s(dbname, PATH_MAX, szFileName);
	}


	void SetDbNameState(int wm, int wk, int bm, int bk, int wmrank, int bmrank, int color, int state, wchar_t *dbname)
	{
		wcscpy_s(dbname, PATH_MAX, szRawDatabaseDir);
		int len = (int)wcslen(dbname);
		if(dbname[len - 1] != L'\\')
		{
			wcscat_s(dbname, PATH_MAX, L"\\");
		}

		wchar_t szFileName[PATH_MAX];

		if(color == BLACK)
			swprintf_s(szFileName, PATH_MAX,L"db%i%i%i%i-%i%ib.dat.%d", wm,wk,bm,bk,wmrank,bmrank, state);
		else
			swprintf_s(szFileName, PATH_MAX, L"db%i%i%i%i-%i%iw.dat.%d", wm,wk,bm,bk,wmrank,bmrank, state);

		wcscat_s(dbname, PATH_MAX, szFileName);
	}


	void SetDbDTMName(int wm, int wk, int bm, int bk, int wmrank, int bmrank, int color, wchar_t *dbname)
	{
		wcscpy_s(dbname, PATH_MAX, szRawDatabaseDir);
		int len = (int)wcslen(dbname);
		if(dbname[len - 1] != L'\\')
		{
			wcscat_s(dbname, PATH_MAX, L"\\");
		}

		wchar_t szFileName[PATH_MAX];

		if(color == BLACK)
			swprintf_s(szFileName, PATH_MAX,L"db%i%i%i%i-%i%ib.dtm", wm,wk,bm,bk,wmrank,bmrank);
		else
			swprintf_s(szFileName, PATH_MAX, L"db%i%i%i%i-%i%iw.dtm", wm,wk,bm,bk,wmrank,bmrank);

		wcscat_s(dbname, PATH_MAX, szFileName);
	}


	void SetDbDTMNameState(int wm, int wk, int bm, int bk, int wmrank, int bmrank, int color, wchar_t *dbname, int state)
	{
		wcscpy_s(dbname, PATH_MAX, szRawDatabaseDir);
		int len = (int)wcslen(dbname);
		if(dbname[len - 1] != L'\\')
		{
			wcscat_s(dbname, PATH_MAX, L"\\");
		}

		wchar_t szFileName[PATH_MAX];

		if(color == BLACK)
			swprintf_s(szFileName, PATH_MAX,L"db%i%i%i%i-%i%ib.dtm.%d", wm,wk,bm,bk,wmrank,bmrank, state);
		else
			swprintf_s(szFileName, PATH_MAX, L"db%i%i%i%i-%i%iw.dtm.%d", wm,wk,bm,bk,wmrank,bmrank, state);

		wcscat_s(dbname, PATH_MAX, szFileName);
	}


	// "power-cut" saving management of data
	// the EGTB Generator with the help of these functions is able to recover the computing state
	// from the unexpected termination
	/////////////////////////////////////////////////////////////////////////////////////////////////////


	bool CheckStatePresence(int wm, int wk, int bm, int bk, int wmrank, int bmrank, int state)
	{
		wchar_t dbname[PATH_MAX];
		FILE *fp;
		int loadblack = 0;
		int loadwhite = 0;
		int loadDTMwhite = 0;
		int loadDTMblack = 0;
		int symmetric = IsSymmetric(wm, wk, bm, bk, wmrank, bmrank);

		SetDbNameState(wm, wk, bm, bk, wmrank, bmrank, WHITE, state, dbname);
		_wfopen_s(&fp, dbname,L"rb");
		if(fp!=NULL)
		{
			loadwhite = 1;
			fclose(fp);
		}
	
		SetDbNameState(wm, wk, bm, bk, wmrank, bmrank, BLACK, state, dbname);
	
		_wfopen_s(&fp, dbname,L"rb");
		if(fp!=NULL)
		{
			loadblack = 1;
			fclose(fp);
		}	

		SetDbDTMNameState(wm, wk, bm, bk, wmrank, bmrank, WHITE, dbname, state);
		_wfopen_s(&fp, dbname,L"rb");
		if(fp!=NULL)
		{
			loadDTMwhite = 1;
			fclose(fp);
		}
	
		SetDbDTMNameState(wm, wk, bm, bk, wmrank, bmrank, BLACK, dbname, state);
	
		_wfopen_s(&fp, dbname,L"rb");
		if(fp!=NULL)
		{
			loadDTMblack = 1;
			fclose(fp);
		}	


		if(symmetric)
			loadblack = 1, loadDTMblack = 1;

		if(loadblack && loadwhite && loadDTMwhite && loadDTMblack) 
		{
			return true;
		}

		return false;
	}


	int FindMaximumState(int wm, int wk, int bm, int bk, int wmrank, int bmrank, int maxState)
	{
		while(maxState >= 0)
		{
			if(CheckStatePresence(wm, wk, bm, bk, wmrank, bmrank, maxState))
				break;
			maxState--;
		}

		return maxState;

	}

	void DeleteStates(int wm, int wk, int bm, int bk, int wmrank, int bmrank, int maxState)
	{
		wchar_t dbname[PATH_MAX];

		for(int state = 0; state <= maxState; state++)
		{
			// white 
			SetDbNameState(wm, wk, bm, bk, wmrank, bmrank, WHITE, state, dbname);
			_wremove(dbname);

			// black
			SetDbNameState(wm, wk, bm, bk, wmrank, bmrank, BLACK, state, dbname);
			_wremove(dbname);

			// white DTM
			SetDbDTMNameState(wm, wk, bm, bk, wmrank, bmrank, WHITE, dbname, state);
			_wremove(dbname);

			// black DTM
			SetDbDTMNameState(wm, wk, bm, bk, wmrank, bmrank, BLACK, dbname, state);
			_wremove(dbname);

		}
	}


	bool CheckDbPresence(int wm, int wk, int bm, int bk, int wmrank, int bmrank)
	{
		wchar_t dbname[PATH_MAX];
		FILE *fp;
		int loadblack = 0;
		int loadwhite = 0;
		int symmetric = IsSymmetric(wm, wk, bm, bk, wmrank, bmrank);

		SetDbName(wm, wk, bm, bk, wmrank, bmrank, WHITE, dbname);
		_wfopen_s(&fp, dbname,L"rb");
		if(fp!=NULL)
		{
			loadwhite = 1;
			fclose(fp);
		}
	
		SetDbName(wm, wk, bm, bk, wmrank, bmrank, BLACK, dbname);
	
		_wfopen_s(&fp, dbname,L"rb");
		if(fp!=NULL)
		{
			loadblack = 1;
			fclose(fp);
		}	

		if(symmetric)
			loadblack = 1;

		if(loadblack && loadwhite) 
		{
			return true;
		}

		return false;
	}

}
